/*
 * Generated on Apr 18, 2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package cgl.narada.service.record;

import java.util.Enumeration;
import java.util.Hashtable;

import cgl.narada.event.NBEvent;
import cgl.narada.event.NBEventTypes;
import cgl.narada.event.TemplateInfo;
import cgl.narada.event.TemplateProfileAndSynopsisTypes;
import cgl.narada.event.impl.NBEventGenerator;
import cgl.narada.matching.Profile;
import cgl.narada.service.ServiceException;
import cgl.narada.service.ServiceExceptionListener;
import cgl.narada.service.client.ClientService;
import cgl.narada.service.client.EventConsumer;
import cgl.narada.service.client.EventProducer;
import cgl.narada.service.client.NBEventListener;
import cgl.narada.service.client.SessionService;
import cgl.narada.service.reliable.impl.ReliableDeliveryServiceImpl;

/**
 * @author hbulut
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public class RecordServiceImpl extends RecordService implements
    NBEventListener, ServiceExceptionListener {

  private String                      recordTopicString     = "/RecordServices";
  private int                         recordEntityId;
  private String                      moduleName            = "RecordServiceImpl: ";
  private Hashtable                   templates;
  private Hashtable                   entities;
  private Hashtable                   templatesAndEntities;
  private ReliableDeliveryServiceImpl rds;
  private boolean                     initialized           = false;

  private ClientService               clientService;
  private EventConsumer               eventConsumer;
  private EventProducer               eventProducer;
  private static RecordService        recordService         = new RecordServiceImpl();
  private Profile                     consumerProfile;
  private Hashtable                   recordIdentifierTable = new Hashtable();
  private static int                  counter               = 0;

  public static RecordService getInstance() {
    return recordService;
  }

  public boolean isInitialized() {
    return initialized;
  }

  public void initialize(int recordEntityId, ReliableDeliveryServiceImpl rds,
      Hashtable templatesAndEntities, Hashtable entities, Hashtable templates)
      throws ServiceException {
    if (initialized)
      return;
    this.recordEntityId = recordEntityId;
    this.rds = rds;
    this.templatesAndEntities = templatesAndEntities;
    this.entities = entities;
    this.templates = templates;
    initializeProducerConsumer();
    subscrieToRecordServiceTopic();
    initialized = true;
  }

  private void initializeProducerConsumer() throws ServiceException {
    clientService = SessionService.getClientService(recordEntityId);
    if (!clientService.isInitialized()) {
      throw new ServiceException(moduleName + "Client service for entity="
          + recordEntityId + " should have been previously initialized");
    }
    eventConsumer = clientService.createEventConsumer(this);
    eventProducer = clientService.createEventProducer();
  }

  public void subscrieToRecordServiceTopic() throws ServiceException {
    consumerProfile = clientService.createProfile(
        TemplateProfileAndSynopsisTypes.STRING, recordTopicString);
    eventConsumer.subscribeTo(consumerProfile);
  }

  public void onEvent(NBEvent nbEvent) {
    int eventType = nbEvent.getEventType();
    byte[] contentPayload = nbEvent.getContentPayload();
    byte exchangeType = contentPayload[0];
    if (eventType == NBEventTypes.NORMAL_EVENT) {
      System.out.println(moduleName + "Received NORMAL event \n\n");
      return;
    }
    System.out.println(this.moduleName+" templatesANdEntities:\n"+templatesAndEntities.toString());
    if (eventType == NBEventTypes.RECORD_EXCHANGE) {
      if (exchangeType == RecordServiceExchange.RECORD_SERVICE_REQUEST) {
        RecordRequest recordRequest = new RecordRequestEvent(contentPayload);
        synchronized (this) {
          if (recordIdentifierTable.containsKey(recordRequest
              .getRequestIdentifier())) {
            System.out.println("\nrequest repeated. ignoring....");
            return;
          }
        }
        recordIdentifierTable.put(recordRequest.getRequestIdentifier(),
            recordRequest.getRequestIdentifier());
        System.out.println("\n***********\t" + counter + "\t***********");
        counter++;
        System.out.println("\n" + moduleName + "Received record request \n"
            + recordRequest.toString());
        processRecordRequest(recordRequest);
        return;
      }
      System.out.println(moduleName + "Unknown exchange type [" + exchangeType
          + "] received. ");
      return;
    }
    System.out.println(moduleName + "Unknown event type (" + eventType
        + ") received.");
  }

  public void onServiceException(ServiceException serviceException) {
    System.out.println(moduleName + serviceException);
  }

  public boolean containsTemplate(Object templateKey) {
    return templates.containsKey(templateKey);
  }

  public void addTemplateManagement(TemplateInfo templateInfo) {
    int templateId = templateInfo.getTemplateId();
    Object templateKey = new Integer(templateId);

    if (containsTemplate(templateKey)) {
      return;
    }
    try {
      rds.addTemplateManagement(templateInfo);
    } catch (ServiceException e) {
      // TODO
      // We should not have this exception. Continue adding to the templates.
      // Because it is already in the database
      e.printStackTrace();
    }
//    templates.put(templateKey, templateInfo);
//    templatesAndEntities.put(templateKey, new Hashtable());
  }

  public boolean containsEntity(Object entityKey) {
    return entities.containsKey(entityKey);
  }

  public void registerEntity(int entityId) {
    Object entityKey = new Integer(entityId);
    if (containsEntity(entityKey)) {
      return;
    }
    try {
      rds.registerEntity(entityId);
    } catch (ServiceException e) {
      // TODO
      // We should not have this exception. Continue adding to the entities.
      // Because it is already in the database
      e.printStackTrace();
    }
    entities.put(entityKey, entityKey);
  }

  public String registerEntityForTemplate(int entityId,
      TemplateInfo templateInfo) {
    Object entityKey = new Integer(entityId);
    Object templateKey = new Integer(templateInfo.getTemplateId());
    String result = null;
    if (!containsEntity(entityKey)) {
      this.registerEntity(entityId);
    }
    if (!containsTemplate(templateKey)) {
      this.addTemplateManagement(templateInfo);
    }
    Hashtable entityList = (Hashtable) templatesAndEntities.get(templateKey);
    if (entityList.containsKey(entityKey)) {
      // already registered to the tmeplate. No further action needed
      return result;
    }
    try {
      rds.registerEntityForTemplate(entityId, templateInfo.getTemplateId());
    } catch (ServiceException e) {
      // need to register entity and template again
      result = e.getMessage();
      e.printStackTrace();
    }
    entityList.put(entityKey, entityKey);
    return result;
  }

  public RecordResponse deregisterEntityFromTemplate(RecordRequest recordRequest) {
    Object entityKey = new Integer(recordRequest.getEntityId());
    Object templateKey = new Integer(recordRequest.getTemplateId());
    RecordResponse response = null;
    String additionalInfo = null;
    if (!entities.containsKey(entityKey)) {
      additionalInfo = "entity not found";
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
          additionalInfo);
      return response;
    }
    if (!templates.containsKey(templateKey)) {
      additionalInfo = "template not found";
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
          additionalInfo);
      return response;
    }
//    Hashtable entityList = (Hashtable) templatesAndEntities.get(templateKey);
//    if (!entityList.containsKey(entityKey)) {
//      additionalInfo = "entity not registered to template";
//      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
//          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
//          additionalInfo);
//      return response;
//    }
//    entityList.remove(entityKey);
    try {
      rds.deregisterEntityFromTemplate(recordRequest.getEntityId(), recordRequest.getTemplateId());
    } catch (ServiceException e) {
      // TODO
      e.printStackTrace();
    }
    response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
        recordRequest.getEntityId(), recordRequest.getTemplateId(), true, null);
    return response;
  }

  public RecordResponse deRegisterEntity(RecordRequest recordRequest) {
    String additionalInfo = null;
    Object entityKey = new Integer(recordRequest.getEntityId());
    RecordResponse response = null;
    if (!entities.containsKey(entityKey)) {
      additionalInfo = "entity not found";
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
          additionalInfo);
      return response;
    }
    Enumeration lists = templatesAndEntities.elements();
    while (lists.hasMoreElements()) {
      Hashtable entityList = (Hashtable) lists.nextElement();
      if (entityList.containsKey(entityKey)) {
        entityList.remove(entityKey);
      }
    }/* end while */
    try {
      rds.deRegisterEntity(recordRequest.getEntityId());
    } catch (ServiceException e) {
      // TODO
      additionalInfo = "Exception received from RDS";
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
          additionalInfo);
      e.printStackTrace();
      return response;
    }
    response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
        recordRequest.getEntityId(), recordRequest.getTemplateId(), true, null);
    return response;
  }

  public void processRecordRequest(RecordRequest recordRequest) {
    System.out.println("\nProcessing *****:" + counter);
    int entityId = recordRequest.getEntityId();
    Object entityKey = new Integer(entityId);
    int templateId = recordRequest.getTemplateId();
    Object templateKey = new Integer(templateId);
    RecordResponse response = null;
    //    System.out.println("Request received:\n" + recordRequest.toString());
    //    boolean requestSuccess = true;
    if (recordRequest.getRequestType() == RecordServiceExchange.RECORD_SERVICE_REQUEST_REGISTER) {
      response = this.handleRegistration(recordRequest);
    } else if (recordRequest.getRequestType() == RecordServiceExchange.RECORD_SERVICE_REQUEST_DEREGISTER) {
      response = this.handleDeRegistration(recordRequest);
    } else {
      String additionalInfo = "Request type is not supported.";
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
          additionalInfo);
    }
    try {
      this.issueRecordResponse(response, recordRequest.getReplyTopic());
    } catch (ServiceException e) {
      e.printStackTrace();
    }
    return;
  }

  public RecordResponse handleRegistration(RecordRequest recordRequest) {
    RecordResponse response = null;
    TemplateInfo templateInfo = null;
    String additionalInfo = "";
    try {
      templateInfo = rds.createTemplateInfo(recordRequest.getTemplateId(),
          recordRequest.getContentSynopsisType(), recordRequest
              .getContentSynopsis());
    } catch (ServiceException e) {
      additionalInfo = e.getMessage();
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
          additionalInfo);
      return response;
    }
    Object entityKey = new Integer(recordRequest.getEntityId());
    Object templateKey = new Integer(recordRequest.getTemplateId());

    if (!containsEntity(entityKey)) {
      this.registerEntity(recordRequest.getEntityId());
    } else {
      additionalInfo += "EntityId already registered.\n";
    }
    if (!containsTemplate(templateKey)) {
      this.addTemplateManagement(templateInfo);
    } else {
      additionalInfo += "Template already added.\n";
    }
    String result = this.registerEntityForTemplate(recordRequest.getEntityId(),
        templateInfo);
    if (result != null) {
      additionalInfo = additionalInfo + result;
    }
//    additionalInfo = additionalInfo.trim();
    boolean success = true;
    if (result != null) {
      success = false;
    }
    response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
        recordRequest.getEntityId(), recordRequest.getTemplateId(), success,
        additionalInfo);
    return response;
  }

  public RecordResponse handleDeRegistration(RecordRequest recordRequest) {
    RecordResponse response = null;
    TemplateInfo templateInfo = null;
    String additionalInfo = "";
    boolean success = true;
    try {
      templateInfo = rds.createTemplateInfo(recordRequest.getTemplateId(),
          recordRequest.getContentSynopsisType(), recordRequest
              .getContentSynopsis());
    } catch (ServiceException e) {
      additionalInfo = e.getMessage();
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), false,
          additionalInfo);
      return response;
    }
    Object entityKey = new Integer(recordRequest.getEntityId());
    Object templateKey = new Integer(recordRequest.getTemplateId());

    if (!containsEntity(entityKey)) {
      additionalInfo += "Entity not registered\n";
      success = false;
    }
    if (!containsTemplate(templateKey)) {
      additionalInfo += "Template not found\n";
      success = false;
    }
    if (success) {
      response = this.deregisterEntityFromTemplate(recordRequest);
    } else {
      response = new RecordResponseEvent(recordRequest.getRequestIdentifier(),
          recordRequest.getEntityId(), recordRequest.getTemplateId(), success,
          null);
    }
    return response;
  }

  private void issueRecordResponse(RecordResponse recordResponse,
      String replyTopic) throws ServiceException {
    System.out.println(moduleName + "Issuing record response ...\n"
        + recordResponse.toString());

    NBEvent nbEvent = eventProducer.generateEvent(
        TemplateProfileAndSynopsisTypes.STRING, replyTopic, recordResponse
            .getBytes());
    NBEventGenerator.setEventType(nbEvent, NBEventTypes.RECORD_EXCHANGE);
    eventProducer.publishEvent(nbEvent);
  }

  public static void main(String[] args) {

  }
}